{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第8章 函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-1 函数的定义及运行特点\n",
    "\n",
    "+ 1、用 def 定义函数，需要缩进\n",
    "  格式：`def 函数名(参数列表)：`\n",
    "  + 1）参数列表可以没有\n",
    "  + 2）函数体中可以使用return来返回结果，如果没有return，则认为返回值是None\n",
    "\n",
    "+ 函数注意事项：\n",
    "  + 1、函数定义后要被调用，才能起作用\n",
    "  + 2、必须先定义函数，再调用\n",
    "  + 3、系统有默认递归次数，超出则报错。用 \n",
    "  \n",
    "    ```python\n",
    "    import sys \n",
    "    sys.setrecursionlimit(1000000)\n",
    "    ```\n",
    "    来设定递归次数，可能达不到\n",
    "  + 4、定义函数时，尽量避免和python内置的函数名同名"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-2 如何让函数返回多个结果\n",
    "\n",
    "return 后面的语句不会执行\n",
    "\n",
    "```python\n",
    "def damage(skill1,skill2):\n",
    "  damage1 = skill1*3\n",
    "  damage2 = skill*2+8\n",
    "   return damage1,damage2 # 返回的数据会被包装成元祖\n",
    "\n",
    "damages = damage(3,6)\n",
    "print(type(damages))\n",
    "```\n",
    "\n",
    "\n",
    "用元祖显示返回结果是非常不好的习惯\n",
    "好的方式是：\n",
    "\n",
    "```python\n",
    "skill1_damage , skill2_damage=damage(3,6)\n",
    "print(skill1_damage , skill2_damage)\n",
    "```\n",
    "\n",
    "序列解包：用有意义的变量名称来返回函数的值\n",
    "\n",
    "\n",
    "函数中的return重点：\n",
    "+ 1.return 后面的语句不会执行\n",
    "+ 2.return 若返回多个变量，会以**元组**的形式返回 \n",
    "+ 3.return 返回多个变量，不建议用 `(返回1，返回2)[0]`, `(返回1，返回2)[1]`，建议用 `变量a，变量b = 函数(参数1，参数2)` 的形式对多个返回值进行对应；这种叫做“序列解包”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_split_result(str_tmp):\n",
    "    str1 = str_tmp[:3]\n",
    "    str2 = str_tmp[3:]\n",
    "    return str1, str2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('hel', 'lo world')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_split_result(\"hello world\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hel\n",
      "lo world\n"
     ]
    }
   ],
   "source": [
    "str1, str2 = get_split_result(\"hello world\");\n",
    "print(str1)\n",
    "print(str2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-4 序列解包与链式赋值\n",
    "\n",
    "python多元赋值：\n",
    "```python\n",
    "a, b, c = 1, 2, 3\n",
    "d = 1, 2, 3 # d的数据类型是元祖\n",
    "```\n",
    "序列解包：\n",
    "```python\n",
    "a, b, c = [1, 2, 3]\n",
    "a, b, c = d\n",
    "```\n",
    "注：序列解包时，序列中元素个数必须和变量数相等。\n",
    "链式赋值：\n",
    "```shell\n",
    "x = y = z = 1\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tuple"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = 1,2,3 # 多元赋值默认是元祖\n",
    "type(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3\n"
     ]
    }
   ],
   "source": [
    "a, b, c = d\n",
    "print(a, b, c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-5 必须参数、关键字参数和可变参数\n",
    "\n",
    "参数类型的区别关键在于函数调用而不是定义上：\n",
    "+ 必须参数：函数的参数列表中定义的必须赋值的参数。必须放在参数列表的前面。\n",
    "+ 关键字参数：可以在函数调用时明确指出实参是传给哪个形参的，不一定要按照形参顺序。如：\n",
    "  ```python\n",
    "  def add(x, y):\n",
    "    ...\n",
    "    return result\n",
    "\n",
    "  c = add(y = 3, x = 2)\n",
    "  ```\n",
    "+ 默认参数：没有设置默认值的参数必须传递实参\n",
    "+ 可变参数:如：\n",
    "  \n",
    "  ```python\n",
    "  def demo(*param):\n",
    "    print(param)\n",
    "    print(type(param))\n",
    "\n",
    "  demo(1,2,3,4,5)\n",
    "  ```\n",
    "  \n",
    "  结果为：\n",
    "  \n",
    "  ```shell\n",
    "  1,2,3,4,5\n",
    "  tuple类型\n",
    "  ```\n",
    "  也就是说python会自动将可变参数对应的实参转化成tuple类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 3, 4, 5)\n",
      "<class 'tuple'>\n"
     ]
    }
   ],
   "source": [
    "def demo(a, *param):\n",
    "    print(param)\n",
    "    print(type(param))\n",
    "    \n",
    "demo(1,2,3,4,5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "demo() got multiple values for argument 'a'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-82770fc8ca97>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdemo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 可变参数必须要放在最后面\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: demo() got multiple values for argument 'a'"
     ]
    }
   ],
   "source": [
    "demo(2,3,4,5, a=1) # 可变参数必须要放在最后面"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-6 默认参数\n",
    "\n",
    "+ 1.想让参数拥有默认值，就需要将需要的默认值赋值给形参，在定义时需要注意到的点\n",
    "+ 2.没有给形参传递默认值，则需要传递实参\n",
    "\n",
    "注意的坑：\n",
    "\n",
    "+ 1.不能将非默认参数放置在默认参数之后，不论定义与调用\n",
    "+ 2.如果想让某一个特定参数传递参数应该使用关键字指定要改变的默认参数，否则将会根据默认参数的顺序进行传递\n",
    "+ 3.在传递默认参数时，不可以将关键字参数传递与按顺序传递的方式穿插混用\n",
    "  类似于这种\n",
    "  ```python\n",
    "  print_student_files('果果',college = '光明小学',17,gender = '女')\n",
    "  ``` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"my name is 梁山广, I'm 37 years old, I work in ZTE\""
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def getInfo(name, age = 27, company='Huawei'):\n",
    "    return 'my name is ' + name + \", I'm \" + str(age) + ' years old, I work in ' + company\n",
    "\n",
    "getInfo(\"梁山广\", 37, \"ZTE\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"my name is 梁山广, I'm 47 years old, I work in Huawei\""
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "getInfo(\"梁山广\", 47)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"my name is 王蕊, I'm 27 years old, I work in Huawei\""
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "getInfo(\"王蕊\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-7 可变参数\n",
    "\n",
    "+ 1）定义一个拥有可变参数的函数: 括号内形参前加星号*\n",
    "+ 2）Python会自动把可变参数列表所对应的实参组装成一个tuple\n",
    "+ 3）调用时直接传元组，则返回的结果是一个二维元组；若不想形成二维数组，调用时可以使用*，它将把元组里的元素平铺传递给函数。此时，*的作用是把元组里面的元素平铺出来，传递给函数\n",
    "+ 4）可变参数可以和其他类型参数结合记起来。如可以在定义时加上必须参数，但可变参数必须要放在后面\n",
    "+ 5）函数参数的顺序是，必须参数、可变参数，默认参数必须在最后；当需更改默认参数值，必须用关键字参数给默认参数赋值\n",
    "+ 6）鉴于此，不赞成把函数参数类别设计如此复杂\n",
    "\n",
    "例子见上一节"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-8 关键字可变参数\n",
    "\n",
    "\n",
    "格式：参数前加`**`\n",
    "```python\n",
    "def demo(**param):\n",
    "    for key,value in param.items():\n",
    "    print(key, ':', value)\n",
    "```\n",
    "这样在调用时可以传递多个关键字参数，此时python会将其转化为字典类型dict。若还想传递字典类型而不转化为多维数组，调用时需加上`**`。\n",
    "小技巧：\n",
    "遍历字典类型数据方法：\n",
    "```python\n",
    "for key,value in param.items():\n",
    "    print(key, ':', value)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "王蕊\n",
      "a = A\n",
      "b = B\n",
      "c = C\n"
     ]
    }
   ],
   "source": [
    "def key_options(name = \"梁山广\", **param):\n",
    "    print(name)\n",
    "    for key, val in param.items():\n",
    "        print(key + \" = \" + val)\n",
    "        \n",
    "\n",
    "key_options(\"王蕊\", a=\"A\", b = \"B\", c = \"C\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-9 变量作用域\n",
    "\n",
    "+ 函数外部是全局变量；函数内部是局部变量\n",
    "+ 函数体外部引用局部变量，是不能引用的\n",
    "+ 函数体内部的局部变量，不能改变全局变量\n",
    "+ 在函数体内部，局部变量是不分层级的，不能形成块级作用域，可以互相引用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-10 作用域链\n",
    "\n",
    "+ 1）局部变量会在作用域内被优先使用\n",
    "+ 2）局部变量具有相对性。如c=2这个变量在func1()函数中是局部变量，但对func2()函数来说，却是它的上一级，因此不是局部变量，可以被func2()来引用；\n",
    "+ 3）逐层逐级向外寻找变量的定义，很像一个链条，因此作用域有链式的特性；\n",
    "+ 4）如果是for循环向函数体内寻找变量，算作用域链吗？不算。原因是for循环体不是一个作用域，它对循环体外变量的调用属于函数体内变量调用的正常行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8-11 global关键字\n",
    "\n",
    "作用：在函数外部引用函数内部变量，即把局部变量变成函数外部可引用的变量。\n",
    "ps:`import 导入模块后可以使用模块的全局变量`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "def demo():\n",
    "     global c \n",
    "     c = 2\n",
    "demo()\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
